package com.yao2san.simcasserver.processor;

import com.yao2san.simcasserver.config.ProcessorConfiguration;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections4.MapUtils;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class AbstractDatabaseAuthenticationProcessor extends ProcessorConfiguration implements AuthenticationProcessor {
    @Getter
    @Setter
    protected ProcessorConfiguration processorConfiguration;

    @Getter
    @Setter
    protected NamedParameterJdbcTemplate jdbcTemplate;


    protected Map<String, String> getFieldMapping() {
        return this.processorConfiguration.getFiledMapping();
    }

    protected Map<String, Object> query(String originalSql, Map<String, String> params) {
        Map<String, String> sqlParams = parameterMappingTransformation(params, getFieldMapping());
        List<Map<String, Object>> list = jdbcTemplate.queryForList(originalSql, sqlParams);
        if (list != null && list.size() > 0) {
            return list.get(0);
        } else {
            return null;
        }
    }

    private Map<String, String> parameterMappingTransformation(Map<String, String> param, Map<String, String> mapping) {
        if (param == null || mapping == null) {
            return new HashMap<>();
        }
        Map<String, String> result = new HashMap<>();
        for (Map.Entry<String, String> entry : param.entrySet()) {
            String originKey = entry.getKey();
            String newKey = mapping.get(entry.getKey());
            String value = entry.getValue();
            if (newKey == null) {
                result.put(originKey, value);
            } else {
                result.put(newKey, value);
            }
        }
        return result;
    }

    public void process(AuthenticationObject authenticationObject) {

        before(authenticationObject);

        String originalSql = getQuerySql();

        Map<String, String> sqlParams = authenticationObject.getAuthenticationParameter();
        Map<String, Object> queryResult = query(originalSql, sqlParams);

        Result result = new Result();
        if (queryResult != null) {
            DefaultAuthenticationObject defaultAuthenticationObject = (DefaultAuthenticationObject) result.getAuthenticationObject();
            defaultAuthenticationObject.setAuthenticationParameter(authenticationObject.getAuthenticationParameter());
            defaultAuthenticationObject.setServletRequest(authenticationObject.getServletRequest());
            defaultAuthenticationObject.setServletResponse(authenticationObject.getServletResponse());

            AttributePrincipal attributePrincipal = (AttributePrincipal) result.getPrincipal();
            attributePrincipal.setName(MapUtils.getString(queryResult, getUsernameField()));
            if (result.getAttributes() != null)
                attributePrincipal.getAttributes().putAll(result.getAttributes());
            result.setPrincipal(attributePrincipal);
            result.setAttributes(result.getAttributes());
            result.setSuccess(false);
        }
        authenticate(authenticationObject, queryResult, result);

        after(result);

        redirect((HttpServletResponse) authenticationObject.getServletResponse(), authenticationObject.getServiceUrl());
    }

    private void redirect(HttpServletResponse response, String url) {
        try {
            response.sendRedirect(url);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * the core of authentication, you can rewrite it with yourself
     *
     * @param authenticationObject the object of authentication
     * @return authentication result
     */
    protected abstract void authenticate(AuthenticationObject authenticationObject, Map<String, Object> queryResult, Result result);


    @Override
    public Class<? extends AbstractDatabaseAuthenticationProcessor> getProcessClass() {
        return processorConfiguration.getProcessClass();
    }

    @Override
    public Map<String, String> getFiledMapping() {
        return processorConfiguration.getFiledMapping();
    }

    @Override
    public String getQuerySql() {
        return processorConfiguration.getQuerySql();
    }

    @Override
    public List<String> getOutFields() {
        return processorConfiguration.getOutFields();
    }

    @Override
    public String getUsernameField() {
        return processorConfiguration.getUsernameField();
    }

    @Override
    public String getPasswordField() {
        return processorConfiguration.getPasswordField();
    }

    @Override
    public int getFilterOrder() {
        return processorConfiguration.getFilterOrder();
    }

    @Override
    public Class getEncryptClass() {
        return processorConfiguration.getEncryptClass();
    }
}
